package ua.org.extjsbuilder.parser.runtime;

import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.List;

import ua.org.extjsbuilder.parser.IInstruction;
import ua.org.extjsbuilder.parser.JSErrorHandler;
import ua.org.extjsbuilder.parser.runtime.builder.AbstractRuntimeBuilder;
import ua.org.extjsbuilder.parser.scanner.CodeBlocksScanner;
import ua.org.extjsbuilder.parser.scanner.CommentsAndLiteralsScanner;
import ua.org.extjsbuilder.parser.scanner.LinesScanner;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;

public abstract class AbstractInterpreterTestCase extends TestCase {

	protected static class JSTestErrorHandler implements JSErrorHandler {
			private int maxErrors;
			private int errors;
	
			JSTestErrorHandler(int errorCount) {
				maxErrors = errorCount;
				errors = 0;
			}
	
			public void clear() {}
	
			public void error(String message, int line) {
				errors++;
				System.out.println("ERROR - "+message+" at line "+line);
				if(maxErrors<errors){
					throw new AssertionFailedError("Maximum number of errors reached");
				}
			}
	
			public void warn(String message, int line) {
				System.out.println("WARNING - "+message+" at line "+line);
			}
		}

	public AbstractInterpreterTestCase() {
		super();
	}

	protected abstract String getMockPkg();

	protected List<IInstruction> parseFile(String file, Class<?>...result) throws Exception {
		Reader reader = new InputStreamReader(getClass()
			.getClassLoader().getResourceAsStream(getMockPkg()+file));
		return parse(reader,result);
	}

	protected List<IInstruction> parseString(String str, Class<?>...result) throws Exception {
		return parse(new StringReader(str),result);
	}
	
	protected static void assertInstructions(List<IInstruction> instructions, Class<?>...types){
		if(instructions==null||types.length!=instructions.size()){
			throw new AssertionFailedError("Size of instructions set is not equal to expected.");
		}
		int i=0;
		for(IInstruction instr : instructions){
			assertEquals("Wrong instruction type",types[i++], instr.getClass());
		}
	}

	private List<IInstruction> parse(Reader reader, Class<?>[] expected) throws Exception {
		JSGlobalToken global = new JSGlobalToken(reader);
		JSTestErrorHandler handler = new JSTestErrorHandler(0);
		
		global.scan(new LinesScanner());
		global.scan(new CommentsAndLiteralsScanner());
		global.scan(new CodeBlocksScanner());
		global.createRuntime(null,handler);
		
		return checkResult(expected, global);
	}

	@SuppressWarnings("unchecked")
	private List<IInstruction> checkResult(Class<?>[] expected, JSGlobalToken global)
			throws NoSuchFieldException, IllegalAccessException {
		Field fld = AbstractRuntimeBuilder.class.getDeclaredField("instructions");
		fld.setAccessible(true);
		List<IInstruction> result = (List<IInstruction>)fld.get(global);
		assertNotNull(result);
		checkResult(expected, result);
		return result;
	}

	private void checkResult(Class<?>[] expected, List<IInstruction> result) {
		int i=0;
		for(IInstruction instruction : result){
			if(instruction==null)fail("Expected instruction but got NULL");
			if(i>=expected.length){
				fail("Unexpected "+instruction.getName()+
					" at the end of the sequence");
			}
			assertEquals("Bad sequence:",expected[i++].getName(), 
				instruction.getClass().getName());
		}
		if(result.size()<expected.length){
			fail("Expected "+expected[result.size()].getName()+
					" but end of sequence has been reached");
		}
	}
}